//
//  DrawGLKView.swift
//  SmartPenSwift
//
//  Created by tql on 2021/2/20.
//

import GLKit
import OpenGLES
import OpenGLES.ES2.gl
import OpenGLES.EAGL
import OpenGLES.ES2.glext


struct DrawGLKPoint {
    var vertex : GLKVector3
    var color : GLKVector4
}
//typealias SDrawGLKPoint =   DrawGLKPoint
let MAXNUM_VERTECES : Int = 10000000

 let maxLength : Int = MAXNUM_VERTECES


class DrawGLKView: GLKView {

    var hasDraw : Bool = false
    var  drawImage : UIImage?
    
      var strokeLineColor : GLKVector4 = GLKVector4Make(0, 0, 0, 1)//(v: (0,0,0,1))
      var clearColor: Array<CGFloat> = Array(arrayLiteral: 1,1,1,0)
//    var clearColor: GLKVector4 = GLKVector4Make(1, 1, 1, 0)// Array(arrayLiteral: 1,1,1,0)

    var  g_x0 : Float = 0.0, g_x1 : Float = 0.0, g_x2 : Float = 0.0, g_x3 : Float = 0.0
    var g_y0 : Float = 0.0, g_y1 : Float  = 0.0, g_y2 : Float = 0.0, g_y3 : Float = 0.0
    var g_p0 : Float = 0.0, g_p1 : Float = 0.0, g_p2 : Float = 0.0, g_p3 : Float = 0.0
    var g_vx01: Float = 0.0, g_vy01 : Float = 0.0, g_n_x0 : Float = 0.0, g_n_y0 : Float = 0.0
    var g_vx21: Float = 0.0, g_vy21 : Float = 0.0
    var g_norm : Float = 0.0
    var g_n_x2 : Float = 0.0, g_n_y2  : Float = 0.0
    var gPIndex  : Int = 0
    
    var mycontext : EAGLContext? = EAGLContext.init(api: .openGLES2)!
    var myeffect : GLKBaseEffect? = GLKBaseEffect.init()
    var vertexArray : GLuint = GLuint()
    var vertexBuffer : GLuint = GLuint()
    var LineVertexData : [DrawGLKPoint] = [DrawGLKPoint](repeating: DrawGLKPoint(vertex: GLKVector3(), color: GLKVector4()), count: maxLength)

    var length : UInt =  UInt()
    var penThickness : Float = 1.0
    var previousThickness : Float = 0.0
    var previousPoint : CGPoint = CGPoint.init()
    var premidPoint : CGPoint = CGPoint.init()
    var previousVertex : DrawGLKPoint = DrawGLKPoint.init(vertex: GLKVector3(), color: GLKVector4())
    var currentVelocity : DrawGLKPoint = DrawGLKPoint.init(vertex: GLKVector3(), color: GLKVector4())
    var eagleLayer : CAEAGLLayer = CAEAGLLayer()
    var verIndex : Float = 0
    var isStart : Bool = false
//
     
    var insideColor : UIColor = UIColor.red
    var strokeColor : UIColor {
        get{
            return insideColor
        }
        set{
            debugPrint("update strokecolor\(newValue)")
            insideColor = newValue
            updateStrokeColor()
        }
    }
    
    override init(frame: CGRect) {
        debugPrint("init drawGLKView222")

        super.init(frame: frame)
        self.commonInit()
    }
    override init(frame: CGRect, context: EAGLContext) {
        debugPrint("init drawGLKView")
        super.init(frame: frame, context: context)
        self.commonInit()

    }
    
    required init?(coder: NSCoder) {
//        fatalError("init(coder:) has not been implemented")
        super.init(coder: coder)
        self.commonInit()
    }
    func commonInit(){
       debugPrint("commonInit")
//        self.mycontext
        guard let contenxtmy = mycontext else {
            debugPrint("context is nill");
//            NSException.raise(NSExceptionName.init(rawValue: "nsopengles2contexexceiption"), format: "failed to crate", arguments:  )
            
            
            return
        }
        time(nil)
        self.backgroundColor = UIColor.clear
        self.isOpaque = true
        self.contentScaleFactor = UIScreen.main.scale
        self.context = contenxtmy// self.mycontext!
        self.drawableColorFormat = .RGBA8888
        self.enableSetNeedsDisplay = true
        
        self.drawableDepthFormat = .format24
        self.drawableStencilFormat = .format8
        self.drawableMultisample = .multisample4X
        self.setupGL()
        
        
    }
    func setupGL() -> Void {
        EAGLContext.setCurrent(self.mycontext!)
//        myeffect = GLKBaseEffect.init()
        self.updateStrokeColor()
        glDisable(GLenum(GL_DEPTH_TEST))
        glGenVertexArraysOES(1, &vertexArray)
        glBindVertexArrayOES(vertexArray)
        
        glGenBuffers(1, &vertexBuffer)
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), vertexBuffer)
        
        glBufferData(GLenum(GL_ARRAY_BUFFER), MemoryLayout<DrawGLKPoint>.size * maxLength, LineVertexData, GLenum(GL_DYNAMIC_DRAW))
        
        self.bindShaderAttributes()
        
        let ortho : GLKMatrix4 = GLKMatrix4MakeOrtho(-1, 1, -1, 1,   0.1, 2.0)
        self.myeffect!.transform.projectionMatrix = ortho
        let modelViewMatrix  : GLKMatrix4 = GLKMatrix4MakeTranslation(0, 0, -1)
        self.myeffect!.transform.modelviewMatrix = modelViewMatrix
        
        length = 0
        penThickness  = 0.03
        previousPoint = CGPoint.init(x: -1, y: -1)
        
        
    }
    func updateStrokeColor() -> Void {
        var red : CGFloat = 0, green : CGFloat = 0, blue : CGFloat = 0, alpha : CGFloat = 0, white : CGFloat = 0
        
        
        if  insideColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha) {
            debugPrint("red = \(red) green = \(green) blue = \(blue)  alpha = \(alpha)")
            strokeLineColor = GLKVector4Make((Float)(red), (Float)(green), (Float)(blue), (Float)(alpha))
            
        }else if   insideColor.getWhite(&white, alpha: &alpha) {
            debugPrint("white = \(white) green = \(green) blue = \(blue) ")
            strokeLineColor = GLKVector4Make((Float)(white), (Float)(white), (Float)(white), (Float)(alpha))
        }else{
            debugPrint("00001111000011aaaa")

            strokeLineColor = GLKVector4Make(0, 0, 0, 1)
        }
    }
    var _BGColor : UIColor = .clear
    override var backgroundColor: UIColor?{
        
        get{
            return _BGColor
        }
        set{
            super.backgroundColor = newValue
            debugPrint("set backgroundiage>>>>")
            _BGColor = newValue!
            var red : CGFloat = 0, green : CGFloat = 0, blue : CGFloat = 0, white : CGFloat  = 0, alpha : CGFloat = 0
            if _BGColor.getRed(&red, green: &green, blue: &blue, alpha: &alpha) {
                clearColor[0] = red
                clearColor[1] = green
                clearColor[2] = blue
                clearColor[3] = alpha
            }else if _BGColor.getWhite(&white, alpha: &alpha){
                clearColor[0] = white
                clearColor[1] = white
                clearColor[2] = white
                clearColor[3] = alpha
            }
        }
    }
    func tearDownGL() -> Void {
        debugPrint("teeardowngl")
        glDeleteVertexArrays(1, &vertexArray)
        glDeleteBuffers(1, &vertexBuffer)
        self.myeffect = nil
    }
    
    deinit {
        self.tearDownGL()
        if self.mycontext == EAGLContext.current() {
            EAGLContext.setCurrent(nil)
        }
        self.mycontext = nil
    }
    
    func bindShaderAttributes() -> Void {
        glEnableVertexAttribArray(GLuint(GLKVertexAttrib.position.rawValue))
//        var number = 0
        let ptr = UnsafeRawPointer(bitPattern: MemoryLayout<GLfloat>.stride * 0)// UnsafePointer<GLint>(bitPattern: 0)

        glVertexAttribPointer(GLuint(GLKVertexAttrib.position.rawValue),
                              3,
                              GLenum(GL_FLOAT),
                              GLboolean(GL_FALSE),
                              GLsizei(MemoryLayout<DrawGLKPoint>.size),
                              ptr)
        
        glEnableVertexAttribArray(GLuint(GLKVertexAttrib.color.rawValue))
//        var  charnumber = 16
        let ptr2 = UnsafeRawPointer(bitPattern: MemoryLayout<CChar>.stride * 16)//UnsafePointer<GLint>(bitPattern: 16)

        glVertexAttribPointer(GLuint(GLKVertexAttrib.color.rawValue),
                              4,
                              GLenum(GL_FLOAT),
                              GLboolean(GL_FALSE),
                              GLsizei(MemoryLayout<GLfloat>.size * 8),
                              ptr2)// UnsafePointer<Int>(bitPattern: charnumber))
        
    }
    
    override func draw(_ rect: CGRect) {
        debugPrint("drawdraweeee>>>>")
        glClearColor(GLclampf(clearColor[0]), GLclampf(clearColor[1]),GLclampf( clearColor[2]),GLclampf( clearColor[3]))
        glClear(GLbitfield(GL_COLOR_BUFFER_BIT))
        self.myeffect?.prepareToDraw()
        if length > 2 {
            glBindVertexArrayOES(vertexArray)
            glDrawArrays(GLenum(GL_TRIANGLE_STRIP), 0, GLsizei(length))
            glBlendColor(0, 0, 0, 1)
        }
    }
    func erase() -> Void {
        debugPrint("erase????????????")
        length = 0
        hasDraw = false
        self.setNeedsDisplay()
    }
    func addTriangleStripPointsForPrevious(prev previousPoint : DrawGLKPoint, next nextPoint : DrawGLKPoint) -> Void {
        var toTravel : Float = penThickness / 2.0
        for _ : Int in 0..<2 {
            let perPoint : GLKVector3 = perpendicular(previousPoint, nextPoint)
            let nextPointV : GLKVector3 = nextPoint.vertex
            let ref : GLKVector3 = GLKVector3Add(nextPointV, perPoint)
            
            let distance : Float = GLKVector3Distance(nextPointV, ref)
            debugPrint("distance=== \(distance)")
            var difX : Float = nextPointV.x - ref.x
            var difY : Float = nextPointV.y - ref.y
            let ratio : Float = (distance == 0) ? 0 : -1.0 * (toTravel / distance)
            difX = difX * ratio / Float(self.bounds.size.width)
            difY = difY * ratio / Float(self.bounds.size.width)
            
            let v3 : GLKVector3 = GLKVector3.init(v: (Float( nextPointV.x) + difX, Float(nextPointV.y) + difY, 0))
//            if v3.x < 100 {
//                v3.x = v3.x+100
//            }
//            if v3.y < 100 {
//                v3.y = v3.y+100
//            }
            
            
            let stripPoint : DrawGLKPoint = DrawGLKPoint(vertex: v3, color: strokeLineColor)
            
            addVertex(&length, stripPoint)
            toTravel *= -1
            
            
            
            
            
        }
    }
    
    func startWithScale(_ scale : Float, _ offsetX : Float, _ offsetY : Float, _ penWidth : Int, _ pointX : Float,_ pointY : Float, _ force : Int ) -> Void {
        debugPrint("startwidth:::")
        isStart = true
        gPIndex = 0
        g_x0 = Float(pointX * scale + offsetX)
        g_y0 = Float(pointY * scale + offsetY)
        g_p0 = Float(self.getPenWidth(penWidth, pointZ: force))
        
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), vertexBuffer)
        let startPoint : CGPoint =  CGPoint(x: CGFloat( g_x0), y: CGFloat( g_y0))
        let newThickness : Float = 2.0
        penThickness = newThickness
        previousPoint = startPoint
        let  startVetex : DrawGLKPoint = ViewPointToGL(startPoint, self.bounds, strokeLineColor)
        previousVertex = startVetex
        premidPoint = startPoint
        addTriangleStripPointsForPrevious(prev: previousVertex, next: startVetex)
        self.hasDraw = true
        self.setNeedsDisplay()
        
    }
    
    
    func moveWithScale(_ scale : Float, _ offsetX : Float, _ offsetY : Float, _ penWidth : Int, _ pointX : Float,_ pointY : Float, _ force : Int ) -> Void {
        debugPrint("movewidthscal:::")
        if isStart == false {
            debugPrint("意外返回2")
            return
        }
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), vertexBuffer)
        g_x1  = Float(previousPoint.x)
        g_y1 = Float(previousPoint.y)
        g_p1 = Float(self.getPenWidth(penWidth, pointZ: force))
        
        var newThickness : Float = 3.0
        g_x3 = Float(pointX * scale + offsetX)
        g_y3 = Float(pointY * scale + offsetY)
        if g_x1 == g_x3 && g_y1 == g_y3 {
            debugPrint("意外返回")
            return
        }
        
        g_x2 = (g_x1 + g_x3 ) / 2.0
        g_y2 = (g_y1 + g_y3 ) / 2.0
        g_p2 = (g_p1 + g_p3 ) / 2.0
        newThickness = g_p1
        penThickness = newThickness
        let currentPoint : CGPoint = CGPoint(x: CGFloat(g_x3), y: CGFloat(g_y3))
        let currentMidPoint : CGPoint = CGPoint(x: CGFloat(g_x2), y: CGFloat(g_y2))
        
        for i : Int in 0..<2 {
            let quadPoint : CGPoint = DrawBezierToQuarl(premidPoint, previousPoint, currentMidPoint, Double(i) / 2.0)
            let vertex1 : DrawGLKPoint = ViewPointToGL(quadPoint, self.bounds, strokeLineColor)
            addTriangleStripPointsForPrevious(prev: previousVertex,next: vertex1)
            
            previousVertex = vertex1
        }
        
        previousThickness = penThickness
        premidPoint = currentMidPoint
        previousPoint = currentPoint
        self.setNeedsDisplay()
        
    }
    
    func end() -> Void {
        debugPrint("end:::")
        isStart = false
        glBindBuffer(GLenum(GL_ARRAY_BUFFER), vertexBuffer)
        let point : CGPoint = previousPoint
        let newThickness : Float = g_p3
        penThickness = newThickness
        let vertex : DrawGLKPoint = ViewPointToGL(point, self.bounds, strokeLineColor)
        addVertex(&length, vertex)
        previousVertex = vertex
        addVertex(&length, vertex)
        self.setNeedsDisplay()
        previousPoint = CGPoint(x: 0, y: 0)
        
        
    }
    //  Converted to Swift 5.3 by Swiftify v5.3.17740 - https://swiftify.com/
    // Append vertex to array buffer
    @inline(__always) private func addVertex(_ dotLength: inout UInt, _ vertex: DrawGLKPoint) {
        var vertex = vertex
        debugPrint("dotlength= \(dotLength)")
        if dotLength >= maxLength {
            return
        }
       
        let data = glMapBufferOES(GLenum(GL_ARRAY_BUFFER), GLenum(GL_WRITE_ONLY_OES))
        memcpy((data!) + MemoryLayout<DrawGLKPoint>.size * Int(dotLength ?? 0), &vertex, MemoryLayout<DrawGLKPoint>.size)
        glUnmapBufferOES(GLenum(GL_ARRAY_BUFFER))
        (dotLength) = (dotLength) + 1
        
        debugPrint("change : dotlength= \(dotLength)")
    }

    // 绘制贝塞尔曲线
    func DrawBezierToCurvl(_ startPoint : CGPoint, _ controlPoint1 : CGPoint,
                           _ controlPoint2 : CGPoint,_ endPoint :  CGPoint,_ t : Double) -> CGPoint {
         
        let  a1 : Double = pow(1-t, 3) // pow((1 - t), 3)
        let  a2 : Double = pow((1 - t), 2) * 3 * t
        let  a3 : Double = 3 * t*t*(1 - t)
        let  a4 : Double = t*t*t
        
        let x : Double = a1 * (Double)(startPoint.x) + a2 * (Double)(controlPoint1.x) + a3 * (Double)(controlPoint2.x) + a4 * (Double)(endPoint.x)
        let y : Double = a1 * (Double)(startPoint.y) + a2 * (Double)(controlPoint1.y) + a3 * (Double)(controlPoint2.y) + a4 * (Double)(endPoint.y)
        
        return CGPoint.init(x: x , y: y)
    }
    
    func DrawBezierToQuarl(_ startPoint : CGPoint, _ controlPoint1 : CGPoint, _ endPoint : CGPoint,_ t : Double) -> CGPoint {
        
     
        let  a1 : Double = pow((1 - t), 2);
        let  a2 : Double = 2*t*(1 - t);
        let  a3 : Double = t*t;
        
        return CGPoint.init(x:  a1 * (Double)(startPoint.x) + a2 * (Double)(controlPoint1.x)  + a3 * (Double)(endPoint.x),
                            y:  a1 * (Double)(startPoint.y) + a2 * (Double)(controlPoint1.y)  + a3 * (Double)(endPoint.y))
    }
     
    private func perpendicular(_ p1 : DrawGLKPoint, _ p2 : DrawGLKPoint) -> GLKVector3{
        var  ret : GLKVector3 = GLKVector3.init()
        ret.x = p2.vertex.y - p1.vertex.y
        ret.y = -1 * (p2.vertex.x - p1.vertex.x)
        ret.z = 0
        return ret
    }
    
    private func ViewPointToGL(_ viewPoint : CGPoint, _ bounds : CGRect, _ color : GLKVector4) -> DrawGLKPoint{
         
        var verx : GLKVector3 = GLKVector3.init()
        verx.x = Float((viewPoint.x / bounds.size.width * 2.0 - 1))
        verx.y = Float(((viewPoint.y / bounds.size.height) * 2.0 - 1) * -1)
        verx.z = 0
        
        return DrawGLKPoint.init(vertex: verx, color: color)
    }
    
    func getPenWidth(_ penWidth : Int, pointZ : Int) -> Float {
        var mPenWidth : Float = 1
        if penWidth == 1 {
            if pointZ >= 1 && pointZ <= 70 {
                mPenWidth = 1
            }else if pointZ > 70 && pointZ <= 120 {
                mPenWidth = 1.5
            }else if pointZ > 120 && pointZ <= 170 {
                mPenWidth = 2.0
            }else if pointZ > 170 && pointZ <= 210 {
                mPenWidth = 2.5
            }else{
                mPenWidth  = 3.0
            }
        }else if penWidth == 2{
            if pointZ >= 0 && pointZ <= 30 {
                mPenWidth = 1.5
            }else if pointZ > 30 && pointZ <= 80 {
                mPenWidth = 2
            }else if pointZ > 80 && pointZ <= 110 {
                mPenWidth = 3.0
            }else if pointZ > 110 && pointZ <= 170 {
                mPenWidth = 4.0
            }else if pointZ > 170 && pointZ <= 210 {
                mPenWidth = 5.0
            }else{
                mPenWidth  = 5.5
            }
        }else if penWidth == 3{
            if pointZ >= 0 && pointZ <= 30 {
                mPenWidth = 3
            }else if pointZ > 30 && pointZ <= 80 {
                mPenWidth = 4
            }else if pointZ > 80 && pointZ <= 110 {
                mPenWidth = 5
            }else if pointZ > 110 && pointZ <= 170 {
                mPenWidth = 6
            }else if pointZ > 170 && pointZ <= 190 {
                mPenWidth = 6.5
            }else if pointZ > 190 && pointZ <= 210 {
                mPenWidth = 7
            }else{
                mPenWidth  = 7.5
            }
        }else if penWidth == 4{
            if pointZ >= 0 && pointZ <= 30 {
                mPenWidth = 4
            }else if pointZ > 30 && pointZ <= 40 {
                mPenWidth = 5
            }else if pointZ > 40 && pointZ <= 50 {
                mPenWidth = 6
            }else if pointZ > 50 && pointZ <= 70 {
                mPenWidth = 7
            }else if pointZ > 70 && pointZ <= 90 {
                mPenWidth = 8
            }else if pointZ > 90 && pointZ <= 110 {
                mPenWidth = 9
            }else if pointZ > 110 && pointZ <= 170 {
                mPenWidth = 10
            }else if pointZ > 170 && pointZ <= 190 {
                mPenWidth = 10.5
            }else if pointZ > 190 && pointZ <= 210 {
                mPenWidth = 11
            }else{
                mPenWidth  = 11.5
            }
        }else if penWidth == 5{
            if pointZ >= 0 && pointZ <= 30 {
                mPenWidth = 3
            }else if pointZ > 30 && pointZ <= 40 {
                mPenWidth = 5
            }else if pointZ > 40 && pointZ <= 50 {
                mPenWidth = 7
            }else if pointZ > 50 && pointZ <= 70 {
                mPenWidth = 8
            }else if pointZ > 70 && pointZ <= 90 {
                mPenWidth = 9
            }else if pointZ > 90 && pointZ <= 110 {
                mPenWidth = 10
            }else if pointZ > 110 && pointZ <= 170 {
                mPenWidth = 11
            }else if pointZ > 170 && pointZ <= 190 {
                mPenWidth = 11.5
            }else if pointZ > 190 && pointZ <= 210 {
                mPenWidth = 12
            }else{
                mPenWidth  = 12.5
            }
        }else if penWidth >= 6{
            if pointZ >= 0 && pointZ <= 30 {
                mPenWidth = 4
            }else if pointZ > 30 && pointZ <= 40 {
                mPenWidth = 6
            }else if pointZ > 40 && pointZ <= 50 {
                mPenWidth = 8
            }else if pointZ > 50 && pointZ <= 70 {
                mPenWidth = 9
            }else if pointZ > 70 && pointZ <= 90 {
                mPenWidth = 10
            }else if pointZ > 90 && pointZ <= 110 {
                mPenWidth = 11
            }else if pointZ > 110 && pointZ <= 170 {
                mPenWidth = 12
            }else if pointZ > 170 && pointZ <= 190 {
                mPenWidth = 12.5
            }else if pointZ > 190 && pointZ <= 210 {
                mPenWidth = 13
            }else{
                mPenWidth  = 13.5
            }
        }
        
        return mPenWidth
    }
    
}
